package com.wfh.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wfh.constant.RedisConstant;
import com.wfh.constant.ThumbConstant;
import com.wfh.manager.cache.CacheManager;
import com.wfh.model.dto.thumb.DoThumbRequest;
import com.wfh.model.entity.Blog;
import com.wfh.model.entity.Thumb;
import com.wfh.model.entity.User;
import com.wfh.service.BlogService;
import com.wfh.service.ThumbService;
import com.wfh.mapper.ThumbMapper;
import com.wfh.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.TransactionTemplate;


import java.util.concurrent.TimeUnit;
/**
* @author lenovo
* @description 针对表【thumb】的数据库操作Service实现
* @createDate 2025-04-18 17:26:50
*/
@Service("thumbServiceLocalCache")
@RequiredArgsConstructor
public class ThumbServiceImpl extends ServiceImpl<ThumbMapper, Thumb>
    implements ThumbService {

    private final UserService userService;

    private final BlogService blogService;

    private final TransactionTemplate transactionTemplate;

    private final RedisTemplate<String, Object> redisTemplate;

    private final CacheManager cacheManager;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 点赞
     *
     * @param doThumbRequest
     * @param request
     * @return
     */
    @Override
    public Boolean doThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
            throw new RuntimeException("请求参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        String key = RedisConstant.DOTHUMB_LOCK + loginUser.getId().toString().intern();
        // 对点赞操作进行加锁，防止一个用户在短时间内，多次给一个博客进行点赞（也就是防止用户并发请求）

        // 获取redis分布式锁
        RLock lock = redissonClient.getLock(key);
        // 尝试获取锁,(参数：获取锁的最大等待时间，锁自动释放的时间，时间单位)
        try {
            if (lock.tryLock(1, 10, TimeUnit.SECONDS)){
                // 执行点赞业务
                // 编程式事务
                try {
                    return transactionTemplate.execute(status -> {
                        Long blogId = doThumbRequest.getBlogId();
                        boolean exists = this.hasThumb(blogId, loginUser.getId());
                        if (exists) {
                            throw new RuntimeException("用户已经点赞");
                        }
                        boolean update = blogService.lambdaUpdate()
                                .eq(Blog::getId, blogId)
                                .setSql("thumbCount = thumbCount + 1")
                                .update();
                        Thumb thumb = new Thumb();
                        thumb.setUserId(loginUser.getId());
                        thumb.setBlogId(blogId);
                        // 更新博客点赞数量和插入点赞记录都成功的时候才会提交事务
                        boolean success = update && this.save(thumb);

                        // 将点赞记录存入redis
                        if (success){
                            String hasKey = ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId();
                            String fieldKey = blogId.toString();
                            Long realThumbId = thumb.getId();
                            redisTemplate.opsForHash().put(hasKey, fieldKey, realThumbId);
                            cacheManager.putIfPresent(hasKey, fieldKey, realThumbId);
                        }
                        // 更新成功才执行
                        return success;
                    });
                } catch (TransactionException e) {
                    e.printStackTrace();
                } finally {
                    // 判断如果当前锁还没有释放并且当前锁是由当前线程创建的，就释放
                    if (lock.isLocked() && lock.isHeldByCurrentThread()){
                        lock.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        throw new RuntimeException("请求过于频繁，请稍后重试");
    }

    /**
     * 取消点赞
     *
     * @param doThumbRequest
     * @param request
     * @return
     */
    @Override
    public Boolean undoThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
            throw new RuntimeException("请求参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        String key = RedisConstant.UNTHUMB_LOCK + loginUser.getId().toString().intern();
        // 获取redis分布式锁
        RLock lock = redissonClient.getLock(key);
        // 同样，为了防止一个用户同一时间对同一个博客取消点赞，也就是并发安全问题，也需要加锁，和点赞功能一样

        // 加锁
        try {
            if (lock.tryLock(1, 10, TimeUnit.SECONDS)){
                // 取消点赞逻辑
                try {
                    // 编程式事务
                    return transactionTemplate.execute(status -> {
                        Long blogId = doThumbRequest.getBlogId();
                        Long thumbId = ((Long) redisTemplate.opsForHash().get(ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId().toString(), blogId.toString()));
                        Object thumbIdObj = cacheManager.get(ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId(), blogId.toString());

                        if (thumbIdObj == null || thumbIdObj.equals(ThumbConstant.UN_THUMB_CONSTANT)){
                            throw new RuntimeException("用户未点赞");
                        }
                        boolean update = blogService.lambdaUpdate()
                                .eq(Blog::getId, blogId)
                                .setSql("thumbCount = thumbCount - 1")
                                .update();

                        // 更新博客点赞数量和插入取消点赞记录都成功的时候才会提交事务
                        boolean success = update && this.removeById(thumbId);
                        // 将点赞记录冲redis中删除
                        if (success){
                            String hasKey = ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId();
                            String fieldKey = blogId.toString();
                            redisTemplate.opsForHash().delete(hasKey, fieldKey);
                            cacheManager.putIfPresent(hasKey, fieldKey, ThumbConstant.UN_THUMB_CONSTANT);
                        }
                        return success;
                    });
                } catch (TransactionException e) {
                    e.printStackTrace();
                } finally {
                    // 判断如果当前锁还没有释放并且当前锁是由当前线程创建的，就释放
                    if (lock.isLocked() && lock.isHeldByCurrentThread()){
                        lock.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        throw new RuntimeException("请求过于频繁，请稍后重试");
    }

    /**
     * 是否已经点赞
     * @param blogId
     * @param userId
     * @return
     */
    @Override
    public Boolean hasThumb(Long blogId, Long userId) {
        Object thumbIdObj = cacheManager.get(ThumbConstant.USER_THUMB_KEY_PREFIX + userId, blogId.toString());
        if (thumbIdObj == null){
            return false;
        }
        Long thumbId = (Long) thumbIdObj;
        return !thumbId.equals(ThumbConstant.UN_THUMB_CONSTANT);
    }
}




